using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using mshtml;
using OFX.Aggregator.WebScraping.Infrastructure;

namespace OFX.Aggregator.WebScraping.Implementation
{
	internal class HtmlElementCollectionWrapper : IHtmlElementCollection
	{
		private readonly HtmlDocumentManager documentManager;
		private readonly IHTMLElementCollection nativeCollection;

		[DebuggerStepThrough]
		public HtmlElementCollectionWrapper(IHTMLElementCollection nativeCollection, HtmlDocumentManager documentManager)
		{
			this.nativeCollection = nativeCollection;
			this.documentManager = documentManager;
		}

		#region Implementation of IEnumerable

		/// <summary>
		/// Returns an enumerator that iterates through the collection.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
		/// </returns>
		/// <filterpriority>1</filterpriority>
		public IEnumerator<IHtmlElement> GetEnumerator()
		{
			var list = new List<IHtmlElement>(Count);

			for (int index = 0; index < Count; ++index)
			{
				list.Add(this[index]);
			}

			return list.GetEnumerator();
		}

		/// <summary>
		/// Returns an enumerator that iterates through a collection.
		/// </summary>
		/// <returns>
		/// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		[DebuggerStepThrough]
		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		#endregion

		#region Implementation of ICollection<IHtmlElement>

		/// <summary>
		/// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1" />.
		/// </summary>
		/// <param name="item">
		/// The object to add to the <see cref="T:System.Collections.Generic.ICollection`1" />.
		/// </param>
		/// <exception cref="T:System.NotSupportedException">
		/// The <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only.
		/// </exception>
		[DebuggerStepThrough]
		public void Add(IHtmlElement item)
		{
			throw new NotSupportedException();
		}

		/// <summary>
		/// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1" />.
		/// </summary>
		/// <exception cref="T:System.NotSupportedException">
		/// The <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only. 
		/// </exception>
		[DebuggerStepThrough]
		public void Clear()
		{
			throw new NotSupportedException();
		}

		/// <summary>
		/// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1" /> contains a specific value.
		/// </summary>
		/// <returns>
		/// true if <paramref name="item" /> is found in the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false.
		/// </returns>
		/// <param name="item">
		/// The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1" />.
		/// </param>
		[DebuggerStepThrough]
		public bool Contains(IHtmlElement item)
		{
			for (int index = 0; index < Count; ++index)
			{
				if (item == this[index])
					return true;
			}

			return false;
		}

		/// <summary>
		/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1" /> to an <see cref="T:System.Array" />, starting at a particular <see cref="T:System.Array" /> index.
		/// </summary>
		/// <param name="array">
		/// The one-dimensional <see cref="T:System.Array" /> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1" />. The <see cref="T:System.Array" /> must have zero-based indexing.
		/// </param>
		/// <param name="arrayIndex">
		/// The zero-based index in <paramref name="array" /> at which copying begins.
		/// </param>
		/// <exception cref="T:System.ArgumentNullException"><paramref name="array" /> is null.
		/// </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex" /> is less than 0.
		/// </exception>
		/// <exception cref="T:System.ArgumentException"><paramref name="array" /> is multidimensional.
		/// -or-
		/// <paramref name="arrayIndex" /> is equal to or greater than the length of <paramref name="array" />.
		/// -or-
		/// The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1" /> is greater than the available space from <paramref name="arrayIndex" /> to the end of the destination <paramref name="array" />.
		/// -or-
		/// Type <paramref name="T" /> cannot be cast automatically to the type of the destination <paramref name="array" />.
		/// </exception>
		[DebuggerStepThrough]
		public void CopyTo(IHtmlElement[] array, int arrayIndex)
		{
			for (int index = 0; index < Count; ++index)
			{
				array[arrayIndex + index] = this[index];
			}
		}

		/// <summary>
		///                     Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1" />.
		/// </summary>
		/// <returns>
		/// true if <paramref name="item" /> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false. This method also returns false if <paramref name="item" /> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1" />.
		/// </returns>
		/// <param name="item">
		///                     The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1" />.
		///                 </param>
		/// <exception cref="T:System.NotSupportedException">
		///                     The <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only.
		///                 </exception>
		[DebuggerStepThrough]
		public bool Remove(IHtmlElement item)
		{
			throw new NotSupportedException();
		}

		/// <summary>
		///                     Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1" />.
		/// </summary>
		/// <returns>
		///                     The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1" />.
		/// </returns>
		public int Count
		{
			[DebuggerStepThrough]
			get { return this.nativeCollection.length; }
		}

		/// <summary>
		///                     Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only.
		/// </summary>
		/// <returns>
		/// true if the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only; otherwise, false.
		/// </returns>
		public bool IsReadOnly
		{
			get { return true; }
		}

		#endregion

		#region Implementation of IHtmlElementCollection

		[DebuggerStepThrough]
		public IHtmlElementCollection Tags(string tagName)
		{
			return this.documentManager.GetWrapper((IHTMLElementCollection) this.nativeCollection.tags(tagName));
		}

		public IHtmlElement this[int index]
		{
			[DebuggerStepThrough]
			get { return (IHtmlElement) this.documentManager.GetWrapper(this.nativeCollection.item(index, 0)); }
		}

		#endregion
	}
}